package com.tjbklx1.redis.chap06;

import java.util.List;
import java.util.UUID;

import com.tjbklx1.redis.JedisUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.ZParams;

/**
 * 公平信号量
 * @author tjbklx1
 * @date 2018-12-12
 */
public class App64_Semaphore {

	public static void main(String[] args) throws Exception {
		new App64_Semaphore().run();
	}

	private void run() throws Exception {
		Jedis conn = JedisUtil.getJedisWithDB();
		testCountingSemaphore(conn);
	}

	public void testCountingSemaphore(Jedis conn) throws InterruptedException {
		System.out.println("\n----- testCountingSemaphore -----");
		conn.del("testsem", "testsem:owner", "testsem:counter");
		System.out.println("Getting 3 initial semaphores with a limit of 3...");
		for (int i = 0; i < 3; i++) {
			assert acquireFairSemaphore(conn, "testsem", 3, 1000) != null;
		}
		System.out.println("Done!");
		System.out.println("Getting one more that should fail...");
		assert acquireFairSemaphore(conn, "testsem", 3, 1000) == null;
		System.out.println("Couldn't get it!");
		System.out.println();

		System.out.println("Lets's wait for some of them to time out");
		Thread.sleep(2000);
		System.out.println("Can we get one?");
		String id = acquireFairSemaphore(conn, "testsem", 3, 1000);
		assert id != null;
		System.out.println("Got one!");
		System.out.println("Let's release it...");
		assert releaseFairSemaphore(conn, "testsem", id);
		System.out.println("Released!");
		System.out.println();
		System.out.println("And let's make sure we can get 3 more!");
		for (int i = 0; i < 3; i++) {
			assert acquireFairSemaphore(conn, "testsem", 3, 1000) != null;
		}
		System.out.println("We got them!");
		conn.del("testsem", "testsem:owner", "testsem:counter");
	}

	public String acquireFairSemaphore(Jedis conn, String semname, int limit, long timeout) {
		String identifier = UUID.randomUUID().toString();
		String czset = semname + ":owner";
		String ctr = semname + ":counter";

		long now = System.currentTimeMillis();
		Transaction trans = conn.multi();
		trans.zremrangeByScore(semname.getBytes(), "-inf".getBytes(), String.valueOf(now - timeout).getBytes());
		ZParams params = new ZParams();
		params.weights(1, 0);
		trans.zinterstore(czset, params, czset, semname);
		trans.incr(ctr);
		List<Object> results = trans.exec();
		int counter = ((Long) results.get(results.size() - 1)).intValue();

		trans = conn.multi();
		trans.zadd(semname, now, identifier);
		trans.zadd(czset, counter, identifier);
		trans.zrank(czset, identifier);
		results = trans.exec();
		int result = ((Long) results.get(results.size() - 1)).intValue();
		if (result < limit) {
			return identifier;
		}

		trans = conn.multi();
		trans.zrem(semname, identifier);
		trans.zrem(czset, identifier);
		trans.exec();
		return null;
	}

	public boolean releaseFairSemaphore(Jedis conn, String semname, String identifier) {
		Transaction trans = conn.multi();
		trans.zrem(semname, identifier);
		trans.zrem(semname + ":owner", identifier);
		List<Object> results = trans.exec();
		return (Long) results.get(results.size() - 1) == 1;
	}
}
